﻿using NUnit.Framework;
using System.Linq;
using gbL.NetBoard.Broadcast;
using System.Collections.Generic;

namespace gbL.NetBoard.Tests
{
    [TestFixture]
    public class BlackboardTests
    {
        [Test]
        public void Should_be_able_to_add_an_entry_and_retrieve_it()
        {
            // A
            Blackboard target = new Blackboard();
            target.Add("test");
            // A
            var found = target.Read();
            // A
            Assert.That(found, Has.Count.EqualTo(1));
            Assert.That(found[0].Message, Is.EqualTo("test"));
        }

        [Test]
        public void Should_be_able_to_add_an_entry_and_retrieve_it_using_its_tag()
        {
            // A
            Blackboard target = new Blackboard();
            object tag = new object();
            object from = "me";
            object message = "Here is the message";
            // A
            target.Add(tag, from, message);
            var actual = target.ReadWithTag(tag);
            // A
            Assert.That(actual.Count, Is.EqualTo(1));
            Assert.That(actual[0].Tag, Is.EqualTo(tag));
            Assert.That(actual[0].Message, Is.EqualTo("Here is the message"));
        }

        [Test]
        public void Should_be_able_to_add_an_entry_and_take_it_using_its_tag()
        {
            // A
            Blackboard target = new Blackboard();
            object tag = new object();
            object from = "me";
            object message = "Message 2";
            // A
            target.Add(tag, from, message);
            var actual = target.TakeWithTag(tag);
            // A
            Assert.That(actual[0].Message, Is.EqualTo("Message 2"));
            Assert.That(target.ReadWithTag(tag), Has.Count.EqualTo(0));
        }

        [Test]
        public void Should_be_able_to_add_an_entry_and_read_it_using_message_selector()
        {
            // A
            Blackboard target = new Blackboard();
            object tag = new object();
            object from = "me";
            object message = "Message 3";
            // A
            target.Add(tag, from, message);
            var actual = target.Read(e => e.Message.Equals("Message 3"));
            // A
            Assert.That(actual, Has.Count.EqualTo(1));
            Assert.That(actual[0].Tag, Is.SameAs(tag));
        }

        [Test]
        public void Should_be_able_to_add_two_entries_with_same_tag_and_take_them_using_message_selector()
        {
            // A
            Blackboard target = new Blackboard();
            object tag = new object();
            object from = "me";
            object message4 = "Message 4";
            object message5 = "Message 5";
            // A
            target.Add(tag, from, message4);
            target.Add(tag, from, message5);
            var actual = target.Take(e => e.Message.ToString().StartsWith("Message"));
            // A
            Assert.That(actual, Has.Count.EqualTo(2));
            Assert.That(actual.Select(x => x.Message).ToArray(), Is.EquivalentTo(new object[] { message4, message5 }));
            Assert.That(target.ReadWithTag(tag), Has.Count.EqualTo(0));
        }

        [Test]
        public void Should_be_able_to_add_two_entries_with_different_tag_and_take_them_using_message_selector()
        {
            // A
            Blackboard target = new Blackboard();
            object tag1 = new object();
            object tag2 = new object();
            object from = "me";
            object message4 = "Message 4";
            object message5 = "Message 5";
            // A
            target.Add(tag1, from, message4);
            target.Add(tag2, from, message5);
            var actual = target.Take(e => e.Message.ToString().StartsWith("Message"));
            // A
            Assert.That(actual, Has.Count.EqualTo(2));
            Assert.That(actual.Select(x => x.Message).ToArray(), Is.EquivalentTo(new object[] { message4, message5 }));
            Assert.That(target.ReadWithTag(tag1), Has.Count.EqualTo(0));
        }

        [Test]
        public void Should_be_able_to_register_for_Added_event_and_receive_notification_of_added_message()
        {
            // A
            Blackboard target = new Blackboard();
            IBlackboardEntry actual = null;
            object tag = new object();
            // A
            target.Broadcaster.Added += new System.EventHandler<gbL.NetBoard.Broadcast.ChangedEventArgs>(
                (sender, e) => actual = e.Entry
            );
            target.Add(tag, "Me", "Hello");
            // A
            Assert.That(actual, Is.Not.Null);
            Assert.That(actual.Tag, Is.SameAs(tag));
        }

        [Test]
        public void Should_be_able_to_register_for_Removed_event_and_receive_notification_of_taken_message()
        {
            // A
            Blackboard target = new Blackboard();
            IBlackboardEntry actual = null;
            object tag = new object();
            target.Add(tag, "Me", "Hello");

            // A
            target.Broadcaster.Removed += new System.EventHandler<ChangedEventArgs>(
                (sender, e) => actual = e.Entry
            );
            target.TakeWithTag(tag);
            // A
            Assert.That(actual, Is.Not.Null);
            Assert.That(actual.Tag, Is.SameAs(tag));
        }

        [Test]
        public void Should_be_able_to_register_for_Removed_event_and_receive_notification_of_taken_by_selector_message()
        {
            // A
            Blackboard target = new Blackboard();
            IBlackboardEntry actual = null;
            object tag = new object();
            target.Add(tag, "Me", "Hello");

            // A
            target.Broadcaster.Removed += new System.EventHandler<ChangedEventArgs>(
                (sender, e) => actual = e.Entry
            );
            target.Take(e => e.Message.Equals("Hello"));
            // A
            Assert.That(actual, Is.Not.Null);
            Assert.That(actual.Tag, Is.SameAs(tag));
        }

        [Test]
        public void Should_be_able_to_take_entry()
        {
            // A
            Blackboard target = new Blackboard();
            IBlackboardEntry lastEntry = null;
            target.Add("Test123");
            target.Broadcaster.Added += new System.EventHandler<ChangedEventArgs>(
                (sender, e) => lastEntry = e.Entry
            );
            target.Add("Test234");
            // A
            IBlackboardEntry actual = target.TakeEntry(lastEntry);
            // A
            Assert.That(actual, Is.EqualTo(lastEntry));
            IList<IBlackboardEntry> entries = target.Read();
            Assert.That(entries, Has.Count.EqualTo(1));
            Assert.That(entries.Single().Message, Is.EqualTo("Test123"));
        }

        [Test]
        public void Should_return_null_on_take_non_entry()
        {
            // A
            Blackboard target = new Blackboard();
            IBlackboardEntry nonEntry = new BlackboardEntry("test", "me", "not added to blackboard");
            target.Add("Added to blackboard");
            // A
            IBlackboardEntry actual = target.TakeEntry(nonEntry);
            // A
            Assert.That(actual, Is.Null);
            IList<IBlackboardEntry> entries = target.Read();
            Assert.That(entries, Has.Count.EqualTo(1));
            Assert.That(entries.Single().Message, Is.EqualTo("Added to blackboard"));
        }
    }
}
